#include "lcd.h"

static int lcd_setcolreg(unsigned regno, unsigned red, 
						unsigned green, unsigned blue, 
						unsigned transp, struct fb_info *info);

static struct fb_info *fb_info;
static uint8_t *fb;
static dma_addr_t fb_phy;
static int pseudo_palette[16];
static uint32_t fb_size;
static quem_lcd_t *quem_lcd;

static struct platform_device virtual_dev = {
	.name = "virtual",
	.id = 0,
};

static struct fb_ops lcd_ops = {
	.owner = THIS_MODULE,
	.fb_setcolreg = lcd_setcolreg,
	.fb_fillrect = cfb_fillrect,
	.fb_copyarea = cfb_copyarea,
	.fb_imageblit = cfb_imageblit,
};

static inline unsigned int chan_to_field(unsigned int chan, struct fb_bitfield *bf)
{
	chan &= 0xffff;
	chan >>= 16 - bf->length;
	return chan << bf->offset;
}

static int lcd_setcolreg(unsigned regno, unsigned red, 
						unsigned green, unsigned blue, 
						unsigned transp, struct fb_info *info)
{
	unsigned int val;
	
	if (regno >= 16)
		return -EINVAL;

	val  = chan_to_field(red, &info->var.red);
	val |= chan_to_field(green, &info->var.green);
	val |= chan_to_field(blue, &info->var.blue);

	pseudo_palette[regno] = val;
	return 0;
}

static int __init lcd_init(void)
{
	int result;

	//设置虚拟平台设备的DMA寻址范围
	virtual_dev.dev.coherent_dma_mask = DMA_BIT_MASK(32);
	//注册一个虚拟平台设备，因为后面某些函数要一个device参数
	result = platform_device_register(&virtual_dev);
	if(result != 0)
	{
		printk("register virtual device failed\r\n");
		return result;
	}

	/* 分配 struct fb_info */
	fb_info = framebuffer_alloc(0, &virtual_dev.dev);
	if(!fb_info)
	{
		platform_device_unregister(&virtual_dev);
		printk("alloc fb_info failed\r\n");
		return -ENOMEM;
	}
	/* 初始化 struct fb_info */
	//LCD分辨率
	fb_info->var.xres = 500;
	fb_info->var.yres = 300;
	fb_info->var.xres_virtual = 500;
	fb_info->var.yres_virtual = 300;
	//像素格式,RGB565
	fb_info->var.bits_per_pixel = 16;
	fb_info->var.red.offset = 11;
	fb_info->var.red.length = 5;
	fb_info->var.green.offset = 5;
	fb_info->var.green.length = 6;
	fb_info->var.blue.offset = 0;
	fb_info->var.blue.length = 5;
	
	//分配显存
	fb_size = fb_info->var.xres * fb_info->var.yres * fb_info->var.bits_per_pixel / 8;
	fb = dma_alloc_wc(&virtual_dev.dev, fb_size, &fb_phy, GFP_KERNEL);
	if(!fb)
	{
		framebuffer_release(fb_info);
		platform_device_unregister(&virtual_dev);
		printk("alloc fb_info failed\r\n");
		return -ENOMEM;
	}
	//显存虚拟地址和大小
	fb_info->screen_base = fb;
	fb_info->screen_size = fb_size;

	//ID
	strcpy(fb_info->fix.id, "100ask_lcd");
	//显存大小和物理地址
	fb_info->fix.smem_len = fb_size;
	fb_info->fix.smem_start = fb_phy;
	//一行的显存长度
	fb_info->fix.line_length = fb_info->var.xres * fb_info->var.bits_per_pixel / 8;
	//显示器类型
	fb_info->fix.type = FB_TYPE_PACKED_PIXELS;
	//像素格式
	fb_info->fix.visual = FB_VISUAL_TRUECOLOR;
	//底层操作函数集合
	fb_info->fbops = &lcd_ops;
	//颜色表
	fb_info->pseudo_palette = pseudo_palette;

	/* 注册 struct fb_info */
	result = register_framebuffer(fb_info);
	if(result < 0)
	{
		dma_free_wc(&virtual_dev.dev, fb_size, fb, fb_phy);
		framebuffer_release(fb_info);
		platform_device_unregister(&virtual_dev);
		printk("register lcd failed\r\n");
		return result;
	}

	/* 初始化LCD控制器 */
	quem_lcd = ioremap(0x021C8000, sizeof(quem_lcd_t));
	quem_lcd->fb_base_phys = fb_phy;
	quem_lcd->fb_xres = 500;
	quem_lcd->fb_yres = 300;
	quem_lcd->fb_bpp = 16;

	return 0;
}

static void __exit lcd_exit(void)
{
	/* 注销 struct fb_info */
	unregister_framebuffer(fb_info);
	//释放显存
	dma_free_wc(&virtual_dev.dev, fb_size, fb, fb_phy);
	/* 释放 struct fb_info */
	framebuffer_release(fb_info);
	//注销虚拟平台设备
	platform_device_unregister(&virtual_dev);
	//取消IO映射
	iounmap(quem_lcd);
}

module_init(lcd_init);
module_exit(lcd_exit);

MODULE_LICENSE("GPL");